#include "xmppcore/tlsnegotiant.h"
#include "xmppcore/saslstreamfeature.h"
#include "xmppcore/connector.h"
#include "xmppcore/constants.h"

namespace yoohoo_xmppcore {

TlsNegotiant::TlsNegotiant(Connector* connector, QObject* parent) : Negotiant(connector, parent) {
	_tlsEstablished = false;
	_streamFeatures = createStreamFeatures();
}

void TlsNegotiant::negotiate() {
	connect(_connector, SIGNAL(tlsReady()), this, SLOT(connectorTlsReady()));
	// start tls negotation
	write("<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>");
}

void TlsNegotiant::connectorTlsReady() {
	disconnect(_connector, SIGNAL(tlsReady()), this, SLOT(connectorTlsReady()));
	_tlsEstablished = true;
	Negotiant::negotiate();
}

void TlsNegotiant::connectorReceived(QString xml) {
	if (xml.startsWith("<stream:error>")) // stream error will processed by 'Connection'
		return;

	if (!_tlsEstablished) {
		if (processTlsProceedResponse(xml))
			startTls();
	} else {
		Negotiant::connectorReceived(xml);
	}
}

void TlsNegotiant::startTls() {
	_connector->startTls();
}

bool TlsNegotiant::processTlsProceedResponse(QString& xml) {
	QXmlStreamReader reader(xml);
	reader.readNext(); // start document

	reader.readNext(); // start element
	if (reader.hasError() || !reader.isStartElement()) {
		emit negotiationError(UnknownNegotiationProtocolError);
		return false;
	}

	if (reader.name().toString() == "proceed") {
		if (reader.prefix().toString() == "" && reader.namespaceUri().toString() == XMPP_TLS_NAMESPACE) {
			return true;
		} else {
			emit negotiationError(UnknownNegotiationProtocolError);
			return false;
		}
	} else if (reader.name().toString() == "failure" && reader.namespaceUri().toString() == XMPP_TLS_NAMESPACE) {
		emit tlsFailure();
		return false;
	} else {
		emit negotiationError(UnknownNegotiationProtocolError);
		return false;
	}
}

StreamFeatures* TlsNegotiant::createStreamFeatures() {
	StreamFeatures* streamFeatures = new StreamFeatures;
	streamFeatures->registerSupportedStreamFeature(new SaslStreamFeature);

	return streamFeatures;
}

}
